扩展方法
扩展方法可以扩展类型的功能,但不必修改类型本身。甚至可以使用扩展方法扩展不能修改的类型,包括在.NET Framework中定义的类型。例如,使用扩展方法甚至可以给System.String等基本类型添加功能。
为了扩展类型的功能,需要提供可以通过该类型的实例调用的方法。为此创建的方法称为扩展方法(extension method),它可以带任意数量的参数,返回任意类型(包括void)。要创建和使用扩展方法,必须:
(1)创建一个非泛型静态类。
(2)使用扩展方法的语法,为所创建的类添加扩展方法,作为静态方法(稍后介绍)。
(3)确保使用扩展方法的代码用using语句导入了包含扩展方法类的名称空间。
(4)通过扩展类型的一个实例调用扩展方法,与调用扩展类型的其他方法一样。
C#编译器在第(3)步和第(4)步之间完成了它的使命。IDE会立即发现我们创建了一个扩展方法,并显示在IntelliSense中,如图14-11所示。
在图14-11中,可通过string对象(这里仅是一个字面量字符串)使用扩展方法MyMarvelousExtensionMethod()。这个方法用一个略微不同的方法图标来表示,该图标包含一个向下箭头,这个方法不带其他参数,返回一个字符串。
为了定义扩展方法,应采用与其他方法相同的方式定义一个方法,但该方法必须满足扩展方法的语法要求。这些要求如下:
• 方法必须是静态的。
• 方法必须包含一个参数,表示调用扩展方法的类型实例(该参数在这里称为实例参数)。
• 实例参数必须是为方法定义的第一个参数(与其他方法一样,之后可以定义任意多个参数)。
• 除了this关键字外,实例参数不能有其他修饰符。
扩展方法的语法如下:
public static class ExtensionClass
{
public static <ReturnType> <ExtensionMethodName>(
this <TypeToExtend> instance, <OtherParameters>)
{
...
}
}
导入了包含静态类(其中包括此方法)的名称空间后(也就是使扩展方法变得可用),就可以编写如下代码:
<TypeToExtend> myVar;
// myVar is initialized by code not shown here.
myVar.<ExtensionMethonName>();
还可以在扩展方法中包含需要的其他参数,并使用其返回类型。 这个调用实际上与以下调用相同,但语法更加简单:
<TypeToExtend> myVar;
// myVar is initialized by code not shown here.
ExtensionClass.<ExtensionMethodName>(myVar);
另一个优点是,导入后,就可以通过IntelliSense查看扩展方法,这样能容易地找到需要的功能。扩展方法可能分布在多个扩展类中,甚至分布在多个库中,但它们都会显示在扩展类型的成员列表中。
定义了可以用于某个类型的扩展方法后,还可以把它用于派生于这个类型的子类型。在本章前面的一个示例中,如果为Animal类定义了一个扩展方法,就可以诸如Cow的对象上调用它。
还可以定义在特定接口上执行的扩展方法,接着就可以为实现了该接口的任意类型使用该扩展方法。
扩展方法为在应用程序中重用实用代码库提供了一种方式。它们还可以广泛用于本书后面介绍的LINQ中。为更好地理解它,下面来分析一个示例。
namespace ExtensionLib
{
public static class WordProcessor
{
public static List<string> GetWords(
this string sentence,
bool capitalizeWords = false,
bool reverseOrder = false,
bool reverseWords = false)
{
...
}
...
public static string ToStringReversed(this object inputObject)
{
return ReverseWord(inputObject.ToString());
}
public static string AsSentence(this List<string> words)
{
StringBuilder sb = new StringBuilder();
for(int wordIndex = 0; wordIndex < words.Count; wordIndex++)
{
sb.Append(words[wordIndex]);
if(wordIndex != words.Count - 1)
{
sb.Append(' ');
}
}
return sb.ToString();
}
}
}
修改Program.cs中的代码,如下所示:
using ExtensionLib;
namespace Ch14Ex05
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Enter a string to convert:");
string sourceString = Console.ReadLine();
Console.WriteLine("String with title casing: {0}", sourceString.GetWords(capitalizeWords : true).AsSentence());
Console.WriteLine("String backwards: {0}",
sourceString.GetWords(reverseOrder: true,
reverseWords: true).AsSentence());
Console.WriteLine("String length backwards: {0}", sourceString.Length.ToStringReversed());
Console.ReadKey();
}
}
}
示例的说明
这个示例创建了一个类库,其中包含在一个简单客户应用程序中使用的实用扩展方法。这个类库有一个静态类WordProcessor的扩展版本,WordProcessor来自上一个包含扩展方法的“试一试”示例,我们把包含这个类的名称空间ExtensionLib导入客户应用程序,以便使用这些扩展方法。
我们创建了3个扩展方法,如表14-1所示。
表14-1扩展方法
方法 | 描述 |
---|---|
GetWords() | 操作字符串的灵活方法,如上个示例所示。在这个示例中,这个方法改为扩展方法,返回一个List<string> |
ToStringReversed() | 对于在对象上调用ToString()所返回的字符串,使用ReverseWord()使其中的字母逆序,并返回一个字符串 |
AsSentence() | "铺平"一个List<string> 对象,返回一个字符串,该字符串由它包含的单词组成 |
客户代码一次调用这些方法,以各种方式修改输入的字符产。前面定义的GetWords()方法返回一个List<string>
,所以使用AsSentence()把它的输出结果铺平为一个字符串,以便于使用。
扩展方法ToStringReversed()是一个比较一般的扩展方法,它不需要string类型的实例参数,而是有一个object类型的实例参数。这表示这个扩展方法可以在任意对象上调用,在IntelliSense中会显示在所使用的每个对象上。在这个扩展方法中没有做太多工作,因为不能对要使用的对象做太多假定。可使用is运算符或尝试类型转换,来确定实例参数的类型,并据此执行相应的操作,也可以执行这个例子的操作,使用所有对象都支持的基本功能---ToString()方法:
public static string ToStringReversed(this object inputObject)
{
return ReverseWord(inputObject.ToString());
}
这个方法只是在其实例参数上调用了ToString()方法,用前面的ReverseWord()方法使实例参数中的字母逆序。在示例客户程序中,从int变量上调用了ToStringReversed()方法,得到该整数的一个字符串表示,其中的数字顺序被颠倒了。
可在多个类型上使用的扩展方法非常有用。另外,还可以定义泛型扩展方法,它们可以把约束应用于类型,如第12章所述。
🔚